package
{
	import com.smartfoxserver.v2.entities.data.SFSObject;
	import com.smartfoxserver.v2.entities.Room;
	import com.smartfoxserver.v2.entities.variables.RoomVariable;
	import com.smartfoxserver.v2.entities.variables.SFSRoomVariable;
	import com.smartfoxserver.v2.entities.variables.SFSUserVariable;
	import com.smartfoxserver.v2.requests.LeaveRoomRequest;
	import com.smartfoxserver.v2.requests.SetRoomVariablesRequest;
	import eDpLib.events.ProxyEvent;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.ui.Mouse;
	import org.casalib.events.ProcessEvent;
	import LocalPlayer;
	
	// masiputih imports.
	//import masiputih.movement.AIMove;
	//import masiputih.movement.Wander;
	//import masiputih.SpriteSheet;
	//import masiputih.Tile;
	//import masiputih.World;
	//
	// as3isolib imports.
	import as3isolib.core.ClassFactory;
	import as3isolib.display.IsoView;
	import as3isolib.display.scene.IsoGrid;
	import as3isolib.display.scene.IsoScene;
	import as3isolib.display.primitive.IsoBox;
	import as3isolib.geom.IsoMath;
	import as3isolib.geom.Pt;
	import as3isolib.graphics.Stroke;
	import as3isolib.display.renderers.DefaultShadowRenderer;
	//
	// SmartFox imports.
	//import com.smartfoxserver.v2.SmartFox;
	import com.smartfoxserver.v2.core.SFSEvent;
	//import com.smartfoxserver.v2.controllers.ExtensionController;
	//import com.smartfoxserver.v2.entities.variables.SFSUserVariable;
	import com.smartfoxserver.v2.entities.User;
	//import com.smartfoxserver.v2.entities.*;
	//import com.smartfoxserver.v2.entities.data.*;
	//import com.smartfoxserver.v2.requests.*;
	//import com.smartfoxserver.v2.entities.Room;
	
	/**
	 * ...
	 * @author Bryan Keiren & Sander van Katwijk
	 */
	public class Game 
	{
		static private var m_Singleton:Game = null;
		static public function Get():Game		{	return m_Singleton;		}
		
		// The game grid.
		private var m_Grid:IsoGrid;
		public function getGrid():IsoGrid		{	return m_Grid;	}
		// The scene object.
		private var m_Scene:IsoScene;
		public function getScene():IsoScene		{	return m_Scene;	}
		// The view object.
		private var m_View:IsoView;
		public function getView():IsoView		{	return m_View;	}
		
		private var m_GameEndedTimer:Number = 0;		// seconds.
		private var m_GameEndedTimeToLeave:Number = 5;	// seconds.		
		
		public function Game():void
		{
			Engine.printf("Creating a game.");
		}
		
		public function init():void
		{
			Engine.printf("Initializing game.");
			m_Singleton = this;
			
			var engine:Engine = Engine.Get();
			
			/*
			// Create world.
			world = new World();
			world.debugMode = true;
			world.name = GAME_ROOM_NAME;
			addChild(world);
			*/
			
			/*
			[Embed(source = 'assets/world.jpg')]
			const WORLD_BACKGROUND:Class;
			world.setBackground(new WORLD_BACKGROUND(), -1175, -900);
			*/
			
			m_Grid = new IsoGrid();
			m_Grid.setGridSize(Globals.Grid_Size, Globals.Grid_Size, 1);
			m_Grid.gridlines = new Stroke(1, 0xffffff);
			m_Grid.addEventListener(MouseEvent.CLICK, onMouseClickGrid);
			m_Grid.showOrigin = false;
			m_Grid.cellSize = Globals.Cell_Size;
						
			var factory:ClassFactory = new ClassFactory(DefaultShadowRenderer);
			factory.properties = { shadowColor:0, shadowAlpha:0.4, drawAll:false };
		
			m_Scene = new IsoScene();
			m_Scene.addChild(m_Grid);
			//m_Scene.styleRenderers = [factory];
			m_Scene.render();
			
			m_View = new IsoView();
			m_View.setSize(800, 600);
			m_View.centerOnPt(new Pt(200, 200, 0));
			m_View.addScene(m_Scene);
			engine.addChild(m_View);
			m_View.visible = false;
			
			var plr:LocalPlayer = new LocalPlayer("nullname", new Vector2(10, 10).multiplyN(Globals.Cell_Size), new Team(0x00ff00));
			plr.init();
			
			Globals.EndOfGame_Textfield.x = 100;
			Globals.EndOfGame_Textfield.y = 250;
			Globals.EndOfGame_Textfield.width = 600;
			Globals.EndOfGame_Textfield.height = 25;
			Globals.EndOfGame_Textfield.selectable = false;
			Globals.EndOfGame_Textfield.mouseEnabled = false;
			Globals.EndOfGame_Textfield.textColor = 0xffffff;
			Globals.EndOfGame_Textfield.border = true;
			Globals.EndOfGame_Textfield.background = true;
			Globals.EndOfGame_Textfield.borderColor = 0xffffff;
			Globals.EndOfGame_Textfield.backgroundColor = 0x55555555;
			Globals.EndOfGame_Textfield.visible = false;
			Engine.Get().addChild(Globals.EndOfGame_Textfield);
			
			// Add an event listener for the game that listens to mouse clicks.
			engine.addEventListener(MouseEvent.CLICK, onMouseClick);
			engine.addEventListener(MouseEvent.DOUBLE_CLICK, onMouseDoubleClick);
		}
		
		public function enable():void
		{
			m_View.visible = true;
			LocalPlayer.Get().enable();
		}
		
		public function disable():void
		{
			m_View.visible = false;
			LocalPlayer.Get().disable();
		}
		
		public function update():void
		{
			var gameState:int = Globals.Current_Game_Room.getVariable("gamestate").getIntValue();
			switch (gameState)
			{
				case GameState.Pregame:
				{
					// Just start the game.
					var varArray:Array = [];
					varArray.push( new SFSRoomVariable("gamestate", GameState.InProgress) );
					MessageHandler.Get().sendSetRoomVariablesRequest(varArray, Globals.Current_Game_Room);
					
					Globals.EndOfGame_Textfield.visible = false;
					
					break;
				}
				case GameState.InProgress:
				{
					m_GameEndedTimer = 0;
					
					
					var text:String = "Score:\n";
					for each(var team:Team in Team.getTeams())
					{
						if (team.getNumPlayers() < 1 && team != Team.getDefaultTeam())
						{
							Engine.printf("Empty team");
							//TODO: Remove empty teams.
							team.destroy();
						}
						else
						{
							team.logic();
							if(team != Team.getDefaultTeam())
								text += team.getPlayers()[0].getName() + ": " + Math.floor(team.getScore() / 10) * 10 + "\n";
						}
					}
					
					for each (var flag:Flag in Flag.getFlags())
					{
						flag.logic();
					}
					
					// Increment the local player's play time so that we can store it after the session to keep track of highscores.
					LocalPlayer.Get().incrementPlayTimer(Globals.Delta_Time_S);
					
					// Check whether the local player's score is greater than the score limit, if so, the game should end.
					// This is accomplished by setting the room's room variable 'gamehasended' to true.
					Engine.printf(LocalPlayer.Get().getTeam().getScore() + " ------ " + Globals.Score_Limit);
					if (LocalPlayer.Get().getTeam().getScore() >= Globals.Score_Limit)
					{
						var varArray:Array = [];
						varArray.push( new SFSRoomVariable("gamestate", GameState.Ended) );
						MessageHandler.Get().sendSetRoomVariablesRequest(varArray, Globals.Current_Game_Room);
						
						// Now send a request to the server to update highscores.
						var sfsobj:SFSObject = new SFSObject();
						sfsobj.putInt("timespent", LocalPlayer.Get().getPlayedTime());
						MessageHandler.Get().sendExtensionRequest("storehighscore", sfsobj);
					}
					
					Globals.Ingame_Score_TextField.text = text;
					Globals.Ingame_Score_TextField.setTextFormat(Globals.Text_Format_Consolas20_AlignLeft);
					Globals.EndOfGame_Textfield.visible = false;
					break;
				}
				case GameState.Ended:
				{
					// Increment after-match counter.
					m_GameEndedTimer += Globals.Delta_Time_S;
					
					Globals.EndOfGame_Textfield.text = "Game has ended, leaving session in " + int(m_GameEndedTimeToLeave - m_GameEndedTimer) + " seconds...";
					Globals.EndOfGame_Textfield.setTextFormat(Globals.Text_Format_Consolas20_AlignLeft);
					Globals.EndOfGame_Textfield.visible = true;
					
					if (m_GameEndedTimer >= m_GameEndedTimeToLeave)
					{
						// Leave the room.
						MessageHandler.Get().m_SFS.send( new LeaveRoomRequest(Globals.Current_Game_Room) );
						Engine.Get().SwitchAppState(AppState.LOBBY);
					}
					
					break;
				}
			}
			
		}
		
		public function render():void
		{
			m_Scene.render();
		}
		
		private function printf(a_Msg:String):void
		{
			Engine.printf(a_Msg);
		}
		
		/*
		private function createAnimatedTile( i_name:String, i_posx:Number, i_posy:Number, i_cspan:Number,
											 i_rspan:Number, i_height:Number, i_spriteSheetClass:Class, i_sheetDescriptor:Object):Tile
		{
			var l_tile:Tile = new Tile(i_cspan, i_rspan, i_height);
			l_tile.name = i_name;
			l_tile.selectable = false;
			var l_sprite:SpriteSheet = new SpriteSheet();
			l_sprite.build( new i_spriteSheetClass(), i_sheetDescriptor.frameWidth, i_sheetDescriptor.frameHeight );
			//world.addTile( l_tile, i_posx, i_posy );
			return l_tile;
		}
		*/
		
		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		// Event Handling.
		
		private function onMouseClickGrid(a_Event:ProxyEvent):void
		{
			var mouseEvent:MouseEvent = MouseEvent(a_Event.targetEvent);
			var p:Pt = new Pt(mouseEvent.localX, mouseEvent.localY);
			IsoMath.screenToIso(p);
			
			// TODO: Do stuff with p.x and p.y.
			
			
			//var params:SFSObject = new SFSObject();
			//params.putInt("px", p.x);
			//params.putInt("py", p.y);
			//MessageHandler.Get().sendMessage( "move", params);
		}
		
		private function onMouseClick(a_Event:MouseEvent):void
		{
			
			//var p:Pt = new Pt(mouseEvent.localX, mouseEvent.localY);
			//IsoMath.screenToIso(p);
			
			// TODO: Do stuff with p.x and p.y.
			
			
			//var params:SFSObject = new SFSObject();
			//params.putInt("px", p.x);
			//params.putInt("py", p.y);
			//MessageHandler.Get().sendMessage( "move", params);
		}
		
		private function onMouseDoubleClick(a_Event:ProxyEvent):void
		{
			var mouseEvent:MouseEvent = MouseEvent(a_Event.targetEvent);
		}
		
		public function onUserEnterRoom(evt:SFSEvent):void
		{
			var l_user:User = evt.params.user;
			// TODO: Handle joined user.

			Engine.printf("User " + evt.params.user.name + " entered the room.");
			
			if (Util.strStartsWith(evt.params.room.name, "Session"))
			{
				while (l_user.getVariable("score") == null) { }
				
				var newPlayer:Player = new Player(l_user.name, new Vector2(l_user.getVariable("pos_x").getIntValue(), l_user.getVariable("pos_y").getIntValue()), new Team(Globals.TeamColors[l_user.getVariable("teamid").getIntValue()]));
				
				newPlayer.getTeam().setID(l_user.getVariable("teamid").getIntValue());
				newPlayer.enable();
			}
		}
		public function onUserExitRoom(evt:SFSEvent):void
		{
			var user:User = evt.params.user;
			// TODO: Handle leaving user.
			
			Engine.printf("User " + evt.params.user.name + " left the room (room " + evt.params.room.name + ")");
			
			if (Util.strStartsWith(evt.params.room.name, "session"))
			{
				if (user.name == MessageHandler.Get().m_SFS.mySelf.name)
				{
					Engine.Get().SwitchAppState(AppState.LOBBY);
				}
				else
				{
					// Some other player left the room, delete his player object.
					Player.getPlayerByName(evt.params.user.name).destroy();
				}
			}
		}
		public function onUserVariableUpdate(evt:SFSEvent):void
		{
			//Engine.printf("User variables updated for user " + evt.params.user.name);
			var user:User = evt.params.user as User;
			var changedVars:Array = evt.params.changedVars as Array;
			
			if (user == null)
			{
				user = MessageHandler.Get().m_SFS.mySelf;
			}
			else
			{
				var str:String;
				evt.formatToString(str);
				
				var player:Player = Player.getPlayerByName(evt.params.user.name);
				if (player && player.getUser().name != MessageHandler.Get().m_SFS.mySelf.name)
				{
					for each (var variable:String in changedVars)
					{
						switch (variable)
						{
							case ("pos_x"):
							{
								player.setPosX(user.getVariable("pos_x").getIntValue());
								break;
							}
							case ("pos_y"):
							{
								player.setPosY(user.getVariable("pos_y").getIntValue());
								break;
							}
							case ("target_x"):
							{
								player.setTargetPosX(user.getVariable("target_x").getIntValue());
								break;
							}
							case ("target_y"):
							{
								player.setTargetPosY(user.getVariable("target_y").getIntValue());
								break;
							}
							case ("teamid"):
							{
								player.getTeam().setID(user.getVariable("teamid").getIntValue());
								break;
							}
							case ("score"):
							{
								const score:Number = user.getVariable("score").getDoubleValue();
								if(score > player.getTeam().getScore())
									player.getTeam().setScore(score);
								break;
							}
						}
					}
				}
			}
		}
		public function onRoomVariablesUpdate(evt:SFSEvent):void
		{
			var room:Room = evt.params.room;
			var changedVars:Array = evt.params.changedVars as Array;
			Engine.printf("changedVars = " + changedVars.toString());
			for each(var varName:String in changedVars)
			{
				Engine.printf("Var changed: " + varName);
				var variable:RoomVariable = room.getVariable(varName);
				if (Util.strStartsWith(varName, "node"))
				{
					var flagID:int = int(varName.substr(4, 1));
					
					// TODO: for over 9 flags support, uncomment this and apply for "owner", "x", "y".
					//flagID = int(varName.substr(4, varName.length - 4 - (varName.length - varName.indexOf("y"))));
					
					if (varName.indexOf("owner") != -1)
					{
						var teamID:int = variable.getIntValue();
						Flag.getFlagByID(flagID).attemptCapture(Team.getTeamByID(teamID), true);
					}
					else if (varName.indexOf("x") != -1)
					{
						// X position was updated.
						Flag.getFlagByID(flagID).setX(variable.getIntValue());
					}
					else if (varName.indexOf("y") != -1)
					{
						// Y position was updated.
						
						Flag.getFlagByID(flagID).setY(variable.getIntValue());
					}
				}
			}
		}
	}
}